\chapter{Debug接口}

Debugger 类是Tenon调试功能的接口类，其提供了开发命令行调试或GUI调试功能时，可以使用的公开接口。

可以从Debugger类派生控制器的调试类，通过基类Debugger类的接口实现一般的调试功能。

Debug接口可以大致分为以下几类：

\begin{itemize}
    \item 配置 \quad 在调试开始前，设置所要调试的任务。
    \item 断点 \quad 在调试开始前或者调试时，修改程序断点、数据断点。
    \item 调试 \quad 包括调试的启动、单步执行、暂停、恢复等等。
    \item 查询 \quad 在调试时，查询所调试的任务的状态，比如调用栈、断点等等。
    \item 修改调试状态 \quad 在调试时，修改所调试的任务的状态，比如运动指针、程序指针、变量值等等
\end{itemize}

\section{配置}

\subsection{构造函数}

函数原型为：

\begin{verbatim}
    Debugger::Debugger(Executor* vm);
\end{verbatim}

用于设置调试器所调试的Tenon执行器。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item vm Tenon执行器的实例。
\end{itemize}

\subsection{Debugger::loadTaskCallback}

virtual方法，回调函数，在加载子任务时会调用此方法

\subsection{Debugger::reset}

用于重置调试器的状态、断点设置、数据断点设置等。

\subsection{Debugger::loadTasks}

用于调试开始前，加载任务的二进制文件及运行环境、设置调试上下文环境。

\section{断点}

\subsection{Debugger::addBreakpoint}

函数原型为：

\begin{verbatim}
    bool addBreakpoint(Task* task, std::string_view path, unsigned line,
                       bool enabled, unsigned hitCount = 0,
                       std::string_view msg = "",
                       std::string_view expr = "");
\end{verbatim}

为当前调试添加一个断点设置。添加成功时返回true；添加失败时返回false。
当文件路径/行号错误（例如修改了文件）时，会导致添加失败。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 断点所在的任务。
    \item path 断点所在的文件的路径。
    \item line 断点在文件中的行号。
    \item enabled 断点是否是使能的。
    \item hitCount 断点在触发前需要击中的次数，默认为0。为0时，每次击中都会触发断点
    \item msg 断点击中时不触发程序的中断，而是打印msg对应的字符串。
    \item expr 断点击中时，根据expr表达式的实时值来判断是否触发断点。
\end{itemize}

\subsection{Debugger::deleteBreakpoint}

函数原型为：

\begin{verbatim}
    void deleteBreakpoint(Task* task, std::string_view path, unsigned line);
\end{verbatim}

删除当前调试的一个断点。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 断点所在的任务。
    \item path 断点所在的文件的路径。
    \item line 断点在文件中的行号。
\end{itemize}

\subsection{Debugger::disableBreakpoints}

禁止所有的断点

\subsection{Debugger::enableBreakpoints}

使能所有的断点

\subsection{Debugger::clearBreakpoints}

函数原型为：

\begin{verbatim}
    void clearBreakpoints();
\end{verbatim}

清除当前调试器的所有断点，包括所有任务的断点。

\subsection{Debugger::setBreakpointOnEntry}

函数原型为：

\begin{verbatim}
    void setBreakpointOnEntry(bool enabled);
\end{verbatim}

是否在要加载的任务入口处设置调试断点。

\subsection{Debugger::addWatchpoint}

函数原型为：

\begin{verbatim}
    bool addWatchpoint(Task* task, std::string_view path,
                       unsigned line, std::string_view var);
\end{verbatim}

为当前调试添加一个数据断点设置。添加成功时返回true；添加失败时返回false。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 数据断点所在的任务。
    \item path 数据断点所在的文件的路径。
    \item line 数据断点在文件中的行号。
    \item var 数据断点对应的变量的字符串。
\end{itemize}

\subsection{Debugger::deleteWatchpoint}

函数原型为：

\begin{verbatim}
    void deleteWatchpoint(Task* task, std::string_view path, unsigned line,
                          std::string_view var);
\end{verbatim}

删除一个数据断点。各参数的含义和addWatchpoint相同。

\subsection{Debugger::clearWatchpoints}

函数原型为：

\begin{verbatim}
    void clearWatchpoints();
\end{verbatim}

删除调试器所有的数据断点，包括所有任务的数据断点。

\section{调试}

\subsection{Debugger::run}

函数原型为：

\begin{verbatim}
    void run(Task* debugeeTask);
\end{verbatim}

设置当前调试任务并开始调试执行。注意，所有任务都会开始运行。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::stepOver}

函数原型为：

\begin{verbatim}
    void stepOver(Task* debugeeTask);
\end{verbatim}

在当前调试位置执行单步调试。遇到函数调用时，不进入函数内部。
注意，所有任务都会继续执行，但只执行调试任务的一步，
调试任务执行到达目标位置后，所有的任务都会中止执行；
或者在调试任务执行到达目标位置前，其它的任务触发了中断，
也会导致所有的任务都中止执行。下面的stepIn、stepOut也是类似的情况。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::stepIn}

函数原型为：

\begin{verbatim}
    void stepIn(Task* debugeeTask);
\end{verbatim}

在当前调试位置执行单步调试。遇到函数调用时，进入函数内部。
注意，添加的所有任务都会继续执行，但只执行调试任务的一步。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::stepOut}

函数原型为：

\begin{verbatim}
    void stepOut(Task* debugeeTask);
\end{verbatim}

跳出当前函数的调试执行，并将程序停在该函数调用的下一条语句。
注意，添加的所有任务都会继续执行，但只执行调试任务的一步。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::debugContinue}

函数原型为：

\begin{verbatim}
    void debugContinue(Task* debugeeTask);
\end{verbatim}

继续调试执行所有的任务。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::debugBackward}

函数原型为：

\begin{verbatim}
    void debugBackward(Task* debugeeTask);
\end{verbatim}

单步回退执行调试的任务。注意，只有调试任务会被单步执行，其它的任务不执行。
返回值代表程序中止时调试器的状态。

\subsection{Debugger::debugForward}

函数原型为：

\begin{verbatim}
    void debugForward(Task* debugeeTask);
\end{verbatim}

单步执行调试的任务。注意，只有调试任务会被单步执行，其它的任务不执行。
返回值代表程序中止时调试器的状态。

\section{查询}

\subsection{Debugger::getExecutor}

用于获取调试器对应的执行器的实例

\subsection{Debugger::getStatus}

用于获取调试器的当前状态

\subsection{Debugger::getCurrentModule}

用于获取调试中止时的当前模块

\subsection{Debugger::getCurrentSourceFile}

用于获取调试中止时的当前模块的源文件的路径

\subsection{Debugger::getFirstLine}

用于获取函数的第一行可执行代码所对应的行号

\subsection{Debugger::isInDebugging}

函数原型为：

\begin{verbatim}
    bool isInDebugging();
\end{verbatim}

获取调试器当前是否正处于调试状态。

\subsection{Debugger::getHitBreakpoint}

函数原型为：

\begin{verbatim}
    std::optional<BreakpointHit> getHitBreakpoint(Task* task);
\end{verbatim}

获取任务当前被触发的断点。所有任务都可以执行该操作，
返回的是执行中止时，该任务的中止执行位置。

\subsection{Debugger::getHitWatchpoint}

函数原型为：

\begin{verbatim}
    std::optional<Watchpoint> getHitWatchpoint(Task* task);
\end{verbatim}

获取任务当前被触发的数据断点。所有任务都可以执行该操作。

\subsection{Debugger::getCallStack}

函数原型为：

\begin{verbatim}
    CallStack getCallStack(Task* task);
\end{verbatim}

获取调试中止时任务的函数调用栈。所有任务都可以执行该操作。

\subsection{Debugger::getFrameVarView}

函数原型为：

\begin{verbatim}
    FrameVarView getFrameVarView(Task* task, unsigned frameIndex);
\end{verbatim}


获取程序中止时，函数调用栈中某一栈帧的变量视图。返回值为该栈帧的变量视图。
所有任务都可以执行该操作。
变量视图包括：函数局部变量，所在模块涉及的所有模块变量（模块变量，模块局部变量，全局共享变量，引用的定义在其他模块的变量）。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 查询的任务。
    \item frameIndex 栈帧的序号，从0开始。
\end{itemize}

\section{修改调试状态}

\subsection{Debugger::setProgramPointer}

函数原型为：

\begin{verbatim}
    bool setProgramPointer(Task* task, std::string_view path, unsigned lineNum);
\end{verbatim}

在调试中将程序指针设置到同一个函数里的源代码行号。
设置成功时返回true；设置失败时返回false。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 设置的程序指针所在的任务。
    \item path 函数所在文件的路径。
    \item lineNum 要跳转到的函数源代码的行号。
\end{itemize}

\subsection{Debugger::setMotionPointer}

函数原型为：

\begin{verbatim}
    void setMotionPointer(Task* task, std::string_view file, unsigned mp);
\end{verbatim}

用于设置运动指针的位置

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 设置的运动指针所在的任务。
    \item file 要设置的文件的路径。
    \item mp 要设置的行号位置。
\end{itemize}

\subsection{Debugger::setFrameVarValue}

函数原型为：

\begin{verbatim}
    bool setFrameVarValue(Task* task, unsigned frameIndex,
                          const std::string& expr,
                          const std::string& value);
\end{verbatim}

在程序中止时，设置函数调用栈中某一栈帧里的变量的值。

\vspace{2ex}
\noindent \textbf{参数}
\begin{itemize}
    \item task 设置的变量所在的任务。
    \item frameIndex 栈帧的序号，从0开始。
    \item expr 变量表达式。可以是变量名（形如x），或记录变量的字段（形如x.a），
    或数组变量的元素（形如x{1}），或数组变量的记录元素的字段（形如x{1}.a）。
    \item value 修改后的值，以字符串的形式进行表达。对于布尔型值，可以是true/false；
    对于字符串型值，需要保留字符串前后的双引号。
\end{itemize}
